2 * Copyright (C) 2016 Stefan BrĂ¼ns <stefan.bruens@rwth-aachen.de>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 /* Set the following three defines to your needs */
21 #ifndef SDK_PWM_PERIOD_COMPAT_MODE
22 #define SDK_PWM_PERIOD_COMPAT_MODE 0
24 #ifndef PWM_MAX_CHANNELS
25 #define PWM_MAX_CHANNELS 8
30 /* no user servicable parts beyond this point */
32 #define PWM_MAX_TICKS 0x7fffff
33 #if SDK_PWM_PERIOD_COMPAT_MODE
34 #define PWM_PERIOD_TO_TICKS(x) (x * 0.2)
35 #define PWM_DUTY_TO_TICKS(x) (x * 5)
36 #define PWM_MAX_DUTY (PWM_MAX_TICKS * 0.2)
37 #define PWM_MAX_PERIOD (PWM_MAX_TICKS * 5)
39 #define PWM_PERIOD_TO_TICKS(x) (x)
40 #define PWM_DUTY_TO_TICKS(x) (x)
41 #define PWM_MAX_DUTY PWM_MAX_TICKS
42 #define PWM_MAX_PERIOD PWM_MAX_TICKS
47 #include <eagle_soc.h>
50 // from SDK hw_timer.c
51 #define TIMER1_DIVIDE_BY_16 0x0004
52 #define TIMER1_ENABLE_TIMER 0x0080
55 uint32_t ticks; ///< delay until next phase, in 200ns units
56 uint16_t on_mask; ///< GPIO mask to switch on
57 uint16_t off_mask; ///< GPIO mask to switch off
60 /* Three sets of PWM phases, the active one, the one used
61 * starting with the next cycle, and the one updated
62 * by pwm_start. After the update pwm_next_set
63 * is set to the last updated set. pwm_current_set is set to
64 * pwm_next_set from the interrupt routine during the first
67 typedef struct pwm_phase (pwm_phase_array)[PWM_MAX_CHANNELS + 2];
68 static pwm_phase_array pwm_phases[3];
70 struct pwm_phase* next_set;
71 struct pwm_phase* current_set;
72 uint8_t current_phase;
75 static uint32_t pwm_period;
76 static uint32_t pwm_period_ticks;
77 static uint32_t pwm_duty[PWM_MAX_CHANNELS];
78 static uint16_t gpio_mask[PWM_MAX_CHANNELS];
79 static uint8_t pwm_channels;
81 // 3-tuples of MUX_REGISTER, MUX_VALUE and GPIO number
82 typedef uint32_t (pin_info_type)[3];
85 uint32_t out; /* 0x60000300 */
86 uint32_t out_w1ts; /* 0x60000304 */
87 uint32_t out_w1tc; /* 0x60000308 */
88 uint32_t enable; /* 0x6000030C */
89 uint32_t enable_w1ts; /* 0x60000310 */
90 uint32_t enable_w1tc; /* 0x60000314 */
91 uint32_t in; /* 0x60000318 */
92 uint32_t status; /* 0x6000031C */
93 uint32_t status_w1ts; /* 0x60000320 */
94 uint32_t status_w1tc; /* 0x60000324 */
96 static struct gpio_regs* gpio = (struct gpio_regs*)(0x60000300);
99 uint32_t frc1_load; /* 0x60000600 */
100 uint32_t frc1_count; /* 0x60000604 */
101 uint32_t frc1_ctrl; /* 0x60000608 */
102 uint32_t frc1_int; /* 0x6000060C */
104 uint32_t frc2_load; /* 0x60000620 */
105 uint32_t frc2_count; /* 0x60000624 */
106 uint32_t frc2_ctrl; /* 0x60000628 */
107 uint32_t frc2_int; /* 0x6000062C */
108 uint32_t frc2_alarm; /* 0x60000630 */
110 static struct timer_regs* timer = (struct timer_regs*)(0x60000600);
112 static void ICACHE_RAM_ATTR
113 pwm_intr_handler(void)
115 if ((pwm_state.current_set[pwm_state.current_phase].off_mask == 0) &&
116 (pwm_state.current_set[pwm_state.current_phase].on_mask == 0)) {
117 pwm_state.current_set = pwm_state.next_set;
118 pwm_state.current_phase = 0;
122 // force write to GPIO registers on each loop
123 asm volatile ("" : : : "memory");
125 gpio->out_w1ts = (uint32_t)(pwm_state.current_set[pwm_state.current_phase].on_mask);
126 gpio->out_w1tc = (uint32_t)(pwm_state.current_set[pwm_state.current_phase].off_mask);
128 uint32_t ticks = pwm_state.current_set[pwm_state.current_phase].ticks;
130 pwm_state.current_phase++;
134 // constant interrupt overhead
136 timer->frc1_int &= ~FRC1_INT_CLR_MASK;
137 WRITE_PERI_REG(&timer->frc1_load, ticks);
144 // stop compiler from optimizing delay loop to noop
145 asm volatile ("" : : : "memory");
153 * period: initial period (base unit 1us OR 200ns)
154 * duty: array of initial duty values, may be NULL, may be freed after pwm_init
155 * pwm_channel_num: number of channels to use
156 * pin_info_list: array of pin_info
158 void ICACHE_FLASH_ATTR
159 pwm_init(uint32_t period, uint32_t *duty, uint32_t pwm_channel_num,
160 uint32_t (*pin_info_list)[3])
164 pwm_channels = pwm_channel_num;
165 if (pwm_channels > PWM_MAX_CHANNELS)
166 pwm_channels = PWM_MAX_CHANNELS;
168 for (i = 0; i < 3; i++) {
169 for (j = 0; j < (PWM_MAX_CHANNELS + 2); j++) {
170 pwm_phases[i][j].ticks = 0;
171 pwm_phases[i][j].on_mask = 0;
172 pwm_phases[i][j].off_mask = 0;
175 pwm_state.current_set = pwm_state.next_set = 0;
176 pwm_state.current_phase = 0;
179 // PIN info: MUX-Register, Mux-Setting, PIN-Nr
180 for (n = 0; n < pwm_channels; n++) {
181 pin_info_type* pin_info = &pin_info_list[n];
182 PIN_FUNC_SELECT((*pin_info)[0], (*pin_info)[1]);
183 gpio_mask[n] = 1 << (*pin_info)[2];
184 all |= 1 << (*pin_info)[2];
186 pwm_set_duty(duty[n], n);
188 GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, all);
189 GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, all);
191 pwm_set_period(period);
194 ETS_FRC_TIMER1_NMI_INTR_ATTACH(pwm_intr_handler);
196 ETS_FRC_TIMER1_INTR_ATTACH(pwm_intr_handler, NULL);
198 TM1_EDGE_INT_ENABLE();
200 timer->frc1_int &= ~FRC1_INT_CLR_MASK;
201 timer->frc1_ctrl = 0;
206 __attribute__ ((noinline))
207 static uint8_t ICACHE_FLASH_ATTR
208 _pwm_phases_prep(struct pwm_phase* pwm)
212 uint16_t off_mask = 0;
213 for (n = 0; n < pwm_channels + 2; n++) {
219 for (n = 0; n < pwm_channels; n++) {
220 uint32_t ticks = PWM_DUTY_TO_TICKS(pwm_duty[n]);
222 pwm[0].off_mask |= gpio_mask[n];
223 } else if (ticks >= pwm_period_ticks) {
224 pwm[0].on_mask |= gpio_mask[n];
226 if (ticks < (pwm_period_ticks/2)) {
227 pwm[phases].ticks = ticks;
228 pwm[0].on_mask |= gpio_mask[n];
229 pwm[phases].off_mask = gpio_mask[n];
231 pwm[phases].ticks = pwm_period_ticks - ticks;
232 pwm[phases].on_mask = gpio_mask[n];
233 pwm[0].off_mask |= gpio_mask[n];
238 pwm[phases].ticks = pwm_period_ticks;
240 // bubble sort, lowest to hightest duty
243 if (pwm[n].ticks < pwm[n - 1].ticks) {
244 struct pwm_phase t = pwm[n];
256 for (t = 0; t <= phases; t++) {
257 ets_printf("%d @%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
261 // shift left to align right edge;
262 uint8_t l = 0, r = 1;
263 while (r <= phases) {
264 uint32_t diff = pwm[r].ticks - pwm[l].ticks;
265 if (diff && (diff <= 16)) {
266 uint16_t mask = pwm[r].on_mask | pwm[r].off_mask;
267 pwm[l].off_mask ^= pwm[r].off_mask;
268 pwm[l].on_mask ^= pwm[r].on_mask;
269 pwm[0].off_mask ^= pwm[r].on_mask;
270 pwm[0].on_mask ^= pwm[r].off_mask;
271 pwm[r].ticks = pwm_period_ticks - diff;
272 pwm[r].on_mask ^= mask;
273 pwm[r].off_mask ^= mask;
281 for (t = 0; t <= phases; t++) {
282 ets_printf("%d @%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
288 while (n <= phases) {
289 if (pwm[n].ticks < pwm[n - 1].ticks) {
290 struct pwm_phase t = pwm[n];
302 while (r <= phases) {
303 if (pwm[r].ticks == pwm[l].ticks) {
304 pwm[l].off_mask |= pwm[r].off_mask;
305 pwm[l].on_mask |= pwm[r].on_mask;
311 struct pwm_phase t = pwm[l];
321 for (t = 0; t <= phases; t++) {
322 ets_printf("%d @%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
326 // transform absolute end time to phase durations
327 for (n = 0; n < phases; n++) {
329 pwm[n + 1].ticks - pwm[n].ticks;
330 // subtract common overhead
333 pwm[phases].ticks = 0;
335 // do a cyclic shift if last phase is short
336 if (pwm[phases - 1].ticks < 16) {
337 for (n = 0; n < phases - 1; n++) {
338 struct pwm_phase t = pwm[n];
345 for (t = 0; t <= phases; t++) {
346 ets_printf("%d +%d: %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
354 void ICACHE_FLASH_ATTR
357 pwm_phase_array* pwm = &pwm_phases[0];
359 if ((*pwm == pwm_state.next_set) ||
360 (*pwm == pwm_state.current_set))
362 if ((*pwm == pwm_state.next_set) ||
363 (*pwm == pwm_state.current_set))
366 uint8_t phases = _pwm_phases_prep(*pwm);
368 // all with 0% / 100% duty - stop timer
370 if (pwm_state.next_set) {
372 ets_printf("PWM stop\n");
374 timer->frc1_ctrl = 0;
375 ETS_FRC1_INTR_DISABLE();
377 pwm_state.next_set = NULL;
379 GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (*pwm)[0].on_mask);
380 GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (*pwm)[0].off_mask);
385 // start if not running
386 if (!pwm_state.next_set) {
388 ets_printf("PWM start\n");
390 pwm_state.current_set = pwm_state.next_set = *pwm;
391 pwm_state.current_phase = phases - 1;
392 ETS_FRC1_INTR_ENABLE();
393 RTC_REG_WRITE(FRC1_LOAD_ADDRESS, 0);
394 timer->frc1_ctrl = TIMER1_DIVIDE_BY_16 | TIMER1_ENABLE_TIMER;
398 pwm_state.next_set = *pwm;
401 void ICACHE_FLASH_ATTR
402 pwm_set_duty(uint32_t duty, uint8_t channel)
404 if (channel > PWM_MAX_CHANNELS)
407 if (duty > PWM_MAX_DUTY)
410 pwm_duty[channel] = duty;
413 uint32_t ICACHE_FLASH_ATTR
414 pwm_get_duty(uint8_t channel)
416 if (channel > PWM_MAX_CHANNELS)
418 return pwm_duty[channel];
421 void ICACHE_FLASH_ATTR
422 pwm_set_period(uint32_t period)
426 if (pwm_period > PWM_MAX_PERIOD)
427 pwm_period = PWM_MAX_PERIOD;
429 pwm_period_ticks = PWM_PERIOD_TO_TICKS(period);
432 uint32_t ICACHE_FLASH_ATTR
438 uint32_t ICACHE_FLASH_ATTR
439 get_pwm_version(void)
444 void ICACHE_FLASH_ATTR
445 set_pwm_debug_en(uint8_t print_en)